#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import re

from functools import lru_cache

import requests

from jsonpath import findall

from autoapi.common.errors import AuthError, SendRequestError
from autoapi.common.yaml_handler import read_yaml
from autoapi.core.path_conf import autoapi_path
from autoapi.db.cache_client import cache_client
from autoapi.enums.request.auth import AuthType
from autoapi.utils.enum_control import get_enum_values


class AuthPlugins:
    def __init__(self) -> None:
        self.auth_data = self.get_auth_data()
        self.is_auth = self.auth_data["is_auth"]
        self.auth_type = self.auth_data["auth_type"]
        self.auth_type_verify()
        self.timeout = self.auth_data[f"{self.auth_type}"]["timeout"] or 86400

    @lru_cache
    def get_auth_data(self) -> dict:
        """获取授权数据"""
        auth_data = read_yaml(autoapi_path.auth_conf_dir, filename="auth.yaml")
        return auth_data

    def auth_type_verify(self) -> None:
        """授权类型检查"""
        _allow_auth_type = get_enum_values(AuthType)
        if self.auth_type not in _allow_auth_type:
            raise AuthError(
                f"认证类型错误, 允许 {_allow_auth_type} 之一, 请检查认证配置文件"
            )

    def get_temp_params(self, auth_config: dict) -> dict:
        """获取临时参数"""
        temp_configs = auth_config.get("temp_get_params", [])
        if not temp_configs:
            return {}

        temp_params = {}
        for config in temp_configs:
            if not config.get("enable", False):
                continue

            try:
                url = config["url"]
                method = config["method"].lower()
                headers = config.get("headers", auth_config["headers"])
                params = config.get("params", {})
                json_data = config.get("json", {})
                value_expr = config["value"]
                param_name = config["name"]  # 获取参数名称

                request_data = {
                    "url": url,
                    "headers": headers,
                    "params": params if method == "get" else None,
                    "json": json_data if method == "post" else None,
                    "proxies": {"http": None, "https": None},
                }

                response = requests.request(method=method, **request_data)
                response.raise_for_status()

                extracted_values = findall(value_expr, response.json())
                if not extracted_values:
                    raise AuthError(
                        f"临时参数 '{param_name}' 提取失败，表达式: {value_expr}"
                    )

                temp_params[param_name] = extracted_values[0]
            except Exception as e:
                raise SendRequestError(f"临时参数 '{param_name}' 接口请求异常: {e}")

        return temp_params

    def replace_temp_params(self, login_data: dict, temp_params: dict) -> dict:
        """替换临时参数占位符"""
        for key, value in login_data.items():
            if isinstance(value, str):
                # 查找所有占位符 {{param_name}}
                matches = re.findall(r"\{\{(.*?)\}\}", value)
                for param_name in matches:
                    if param_name in temp_params:
                        # 替换占位符为实际值
                        login_data[key] = value.replace(
                            f"{{{{{param_name}}}}}", str(temp_params[param_name])
                        )
        return login_data

    def request_auth(self) -> requests.Response:
        try:
            auth_config = self.auth_data[f"{self.auth_type}"]
            login_url = auth_config["url"]
            headers = auth_config["headers"]

            # 获取临时参数
            temp_params = self.get_temp_params(auth_config)

            # 构建登录请求数据
            exclude_fields = {
                "url",
                "headers",
                "token_key",
                "timeout",
                "temp_get_params",
            }
            login_data = {
                k: v
                for k, v in auth_config.items()
                if k not in exclude_fields and not isinstance(v, (dict, list))
            }

            # 替换临时参数
            login_data = self.replace_temp_params(login_data, temp_params)

            request_data = {
                "url": login_url,
                "json": login_data,
                "headers": headers,
                "proxies": {"http": None, "https": None},
            }
            response = requests.post(**request_data)
            response.raise_for_status()
        except Exception as e:
            raise SendRequestError(f"授权接口请求响应异常: {e}")
        return response

    @property
    def bearer_token(self) -> str:
        cache_bearer_token = cache_client.get(
            f"{cache_client.token_prefix}:bearer_token", logging=False
        )
        if cache_bearer_token:
            token = cache_bearer_token
        else:
            res = self.request_auth()
            jp_token = findall(
                self.auth_data[f"{self.auth_type}"]["token_key"], res.json()
            )
            token = jp_token[0]
            if not token:
                raise AuthError("Token 获取失败，请检查登录接口响应或 token 提取表达式")
            # 移除token中可能包含的Bearer前缀
            token = token.replace("Bearer ", "")
            cache_client.set(
                f"{cache_client.token_prefix}:bearer_token", token, ex=self.timeout
            )
        return token

    @property
    def bearer_token_custom(self) -> str:
        cache_bearer_token_custom = cache_client.get(
            f"{cache_client.token_prefix}:bearer_token_custom", logging=False
        )
        if cache_bearer_token_custom:
            token = cache_bearer_token_custom
        else:
            token = self.auth_data[f"{self.auth_type}"]["token"]
            cache_client.set(
                f"{cache_client.token_prefix}:bearer_token_custom",
                token,
                ex=self.timeout,
            )
        return token

    @property
    def header_cookie(self) -> dict:
        cache_cookie = cache_client.get(
            f"{cache_client.cookie_prefix}:header_cookie", logging=False
        )
        if cache_cookie:
            cookies = json.loads(cache_cookie)
        else:
            res = self.request_auth()
            res_cookie = res.cookies
            cookies = {k: v for k, v in res_cookie.items()}
            if not cookies:
                raise AuthError("Cookie 获取失败，请检查登录接口响应")
            cache_client.set(
                f"{cache_client.cookie_prefix}:header_cookie",
                json.dumps(cookies, ensure_ascii=False),
                ex=self.timeout,
            )
        return cookies


auth = AuthPlugins()
